<?php

namespace Process;

use Redis;
use Exception;

/**
 * Class RedisClient
 * @package Process
 * @method Redis set(string $key, string $v)
 * @method Redis get(string $key)
 * @method Redis hGet(string $key, $field)
 * @method Redis hGetAll(string $key)
 * @method Redis hKeys(string $key)
 * @method Redis hset(string $key, $field, $val)
 * @method Redis hMset(string $key, array $hash_field)
 * @method Redis hlen(string $key)
 * @method Redis expire(string $key , int $ttl)
 * @method Redis setex(string $key, string | int $ttl, string $value)
 * @method Redis multi( $mode = Redis::MULTI )
 * @method Redis exec( )
 * @method Redis discard( )
 * @method Redis watch( $key )
 */
class RedisClient
{
    public static $client;

    protected $config;

    /**
     * RedisClient constructor.
     * @param $config
     * @throws Exception
     */
    public function __construct($config)
    {
        $this->config = $config;
        $this->reconnect();
        return self::$client;
    }

    /**
     * 入列
     * @param $key
     * @param $value
     * @return mixed
     * @throws Exception
     */
    public function push($key, $value)
    {
        $i = 0;
        try {
            retry:
            $res = self::$client->lPush($key, $value);
        } catch (Exception $e) {
            if ($i > 3 || stripos($e->getMessage(), 'connection') !== false || stripos($e->getMessage(), 'server') !== false)//断线
            {
                throw $e;
            } else {

                $i += 1;
                $this->reconnect(true);
                goto retry;
            }
        }
        return $res;
    }

    /**
     * 入列
     * @param $key
     * @param $value
     * @return mixed
     * @throws Exception
     */
    public function priorityPush($key,$value)
    {
        $i = 0;
        try {
            retry:
            $res = self::$client->rPush($key, $value);
        } catch (Exception $e) {
            if ($i > 3 || stripos($e->getMessage(), 'connection') !== false || stripos($e->getMessage(), 'server') !== false)//断线
            {
                throw $e;
            } else {

                $i += 1;
                $this->reconnect(true);
                goto retry;
            }
        }
        return $res;
    }

    /**
     * 出列
     * @param $key
     * @return mixed
     * @throws Exception
     */
    public function pop($key)
    {
        $i = 0;
        try {
            retry:
            $res = self::$client->rpoplpush($key, "{$key}_COPY");
        } catch (Exception $e) {
            if ($i > 3 || stripos($e->getMessage(), 'connection') !== false || stripos($e->getMessage(), 'server') !== false)//断线
            {
                throw $e;
            } else {

                $i += 1;
                $this->reconnect(true);
                goto retry;
            }
        }
        return $res;
    }

    /**
     * 已经确定完成消费
     * @param $key
     * @param $value
     * @return mixed
     * @throws Exception
     */
    public function dealer($key, $value)
    {
        #todo: 完成确定消费逻辑
        $i = 0;
        try {
            retry:
            $res = self::$client->lRem("{$key}_COPY", $value, 1);
        } catch (Exception $e) {
            if ($i > 3 || stripos($e->getMessage(), 'connection') !== false || stripos($e->getMessage(), 'server') !== false)//断线
            {
                throw $e;
            } else {

                $i += 1;
                $this->reconnect(true);
                goto retry;
            }
        }
        return $res;
    }

    /**
     * 删除队列
     * @param $key
     * @return mixed
     * @throws Exception
     */
    public function delete($key)
    {
        $i = 0;
        try {
            retry:
            $res = self::$client->delete($key) && self::$client->delete("{$key}_COPY");
        } catch (Exception $e) {
            if ($i > 3 || stripos($e->getMessage(), 'connection') !== false || stripos($e->getMessage(), 'server') !== false)//断线
            {
                throw $e;
            } else {

                $i += 1;
                $this->reconnect(true);
                goto retry;
            }
        }
        return $res;
    }

    /**
     * @param $name
     * @param $arguments
     * @return mixed
     * @throws Exception
     */
    public function __call($name, $arguments)
    {
        $i = 0;
        try {
            retry:
            $res = self::$client->{$name}(...$arguments);
        } catch (Exception $e) {
            if ($i > 3 || stripos($e->getMessage(), 'connection') !== false || stripos($e->getMessage(), 'server') !== false)//断线
            {
                throw $e;
            } else {

                $i += 1;
                $this->reconnect(true);
                goto retry;
            }
        }
        return $res;
    }

    /**
     * @param bool $reset
     * @throws Exception
     */
    protected function reconnect($reset = false)
    {
        if ($reset) {
            try {
                self::$client->close();
                self::$client = null;
            } catch (Exception $e) {
            }
            self::$client = null;
        }
        $retry = 3;
        if (!self::$client) {
            reconnect:
            self::$client = new Redis();
            if (!self::$client->pconnect($this->config['host'], $this->config['port'], 80)) {
                throw new Exception("Redis 无法连接");
            }
            if (!self::$client->auth($this->config['auth'])) {
                throw new Exception("Redis 密码错误");
            }
        }
        if (!self::$client->ping()) {
            self::$client->close();
            if ($retry > 0) {
                $retry--;
                sleep(1);
                goto reconnect;
            } else {
                throw new Exception("Redis 无法连接,已经重试三次");
            }
        }
    }
}